module CanCamel
  class Node < ActiveRecord::Base
    self.table_name = TABLE_NAME
    self.inheritance_column = 'type'

    belongs_to :parent, foreign_key: :parent_id, class_name: "CanCamel::Node"
    has_many :children, foreign_key: :parent_id, class_name: "CanCamel::Node"

    has_many :actions, foreign_key: :parent_id, class_name: "CanCamel::ActionNode"
    has_many :groups, foreign_key: :parent_id, class_name: "CanCamel::GroupNode"

    belongs_to :inherited_node, foreign_key: :inherit_id, class_name: "CanCamel::Node"

    def cached_parent
      Cache[path[1..-1]]
    end

    def rank
      raise "inheriting invalid node"
    end

    def cached_inherited_node
      return unless inherit_id
      @inherited_path ||= inherited_node.path
      Cache[@inherited_path]
    end

    def self.with_parents
      joins("LEFT JOIN #{TABLE_NAME} AS parents ON #{TABLE_NAME}.parent_id = parents.id")
    end

    def inherit!(source = nil)
      raise 'inherit nodes only after cache built' unless Cache[path]
      inherit_source! source

      return if inherited

      @inherited = true

      inherit_source! cached_parent
      inherit_source! cached_inherited_node
      # inherit_source! cached_uncle
    end

    validates! :inherited, absence: { message: "You should not save inherited nodes!" }

    def name
      super.to_sym
    end

    def condition
      super.symbolize_keys
    end

    def cached_uncle
      Cache.children_of(cached_parent.cached_parent).select { |x| x.name == name }.first
    end

    attr_writer :path

    def path
      @path ||= parent ? [name] + parent.path  : [name]
    end

    private

    attr_reader :inherited

    def inherit_neighbours!(source)
      Cache.children_of(source).each do |child|
        Cache.append child, path if child.rank > rank
      end
    end

    def inherit_fields!(source)
      inherit_field(source, :description) { |x| self.description ||= x }
      inherit_field(source, :result) { |x| result.merge!(x) { |_k, l, _r| l } }
      inherit_field(source, :condition) { |x| self.condition = x.merge(condition) }
    end

    def inherit_source!(source)
      return unless source

      source.inherit!

      # WTF: if we inherit neighbours only once, it fails. Two times should be fine enought,
      # but we have to do find a way to understand when it enought in code
      # TODO: Refactor
      # FIXME: Refactor
      inherit_neighbours!(source)
      inherit_neighbours!(source)

      inherit_fields!(source) unless source.is_a? Root
    end

    def inherit_field(source, field)
      return if override_fields.include?(field.to_s)
      yield source.send(field)
    end
  end
end
